﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Model4D
{
    enum TransformEnum
    {
        RotateXY,
        RotateXZ,
        RotateXU,
        RotateYZ,
        RotateYU,
        RotateZU,
        Scale
    }

    /// <summary>
    /// Класс генерирующий матрицы для преобразования в многомерном пространстве.
    /// </summary>
    class ModelTransformer
    {
        #region === members ===

        Matrix _matrix = null;

        #endregion

        #region === private ===

        /// <summary>
        /// Формирование матрицы поворота в плоскости осей axe1, axe2.
        /// </summary>
        /// <param name="axe1">Индекс первой оси. X - 0, Y - 1, Z - 2, U -3.</param>
        /// <param name="axe2">Индекс второй оси.</param>
        /// <param name="angle">Угол в радианах.</param>
        /// <returns>Матрица преобразования (вращения).</returns>
        Matrix Rotate(int axe1, int axe2, double angle)
        {
            Matrix matrix = new Matrix(_matrix.Size);
            LoadEdintical(matrix);

            double si = Math.Sin(angle);
            double co = Math.Cos(angle);

            matrix[axe1, axe1] = co;
            matrix[axe1, axe2] = -si;
            matrix[axe2, axe1] = si;
            matrix[axe2, axe2] = co;

            return matrix;
        }

        /// <summary>
        /// Формирование матрицы масштабирования.
        /// </summary>
        /// <param name="c">Параметр масштабирования.</param>
        /// <returns>Матрица преобразования (масштабирования).</returns>
        Matrix Scale(double c)
        {
            Matrix matrix = new Matrix(_matrix.Size);

            for (int i = 0; i < matrix.Size; i++)
            {
                matrix[i, i] = c;
            }

            return matrix;
        }

        /// <summary>
        /// Создание из заданной матрицы единичной.
        /// </summary>
        /// <param name="matrix">Матрица для заполнения.</param>
        void LoadEdintical(Matrix matrix)
        {
            // создаем единичную матрицу
            for (int i = 0; i < matrix.Size; i++)
            {
                for (int j = 0; j < matrix.Size; j++)
                {
                    matrix[i, j] = (i != j) ? 0 : 1;
                }
            }
        }

        #endregion

        /// <summary>
        /// Старт преобразования.
        /// </summary>
        /// <param name="size"></param>
        public void Begin(int size)
        {
            if (_matrix == null || (_matrix.Size != size))
            {
                _matrix = new Matrix(size);
            }

            LoadEdintical(_matrix);
        }

        /// <summary>
        /// Добавить преобразование.
        /// </summary>
        /// <param name="te">Тип преобразования.</param>
        /// <param name="v">Параметр преобразования.</param>
        public void Transform(TransformEnum te, double v)
        {
            Matrix matrix = null;

            switch (te)
            {
                case TransformEnum.RotateXY: 
                    matrix = Rotate(0, 1, v);
                    break;
                case TransformEnum.RotateXZ:
                    matrix = Rotate(0, 2, v);
                    break;
                case TransformEnum.RotateXU:
                    matrix = Rotate(0, 3, v);
                    break;
                case TransformEnum.RotateYZ:
                    matrix = Rotate(1, 2, v);
                    break;
                case TransformEnum.RotateYU:
                    matrix = Rotate(1, 3, v);
                    break;
                case TransformEnum.RotateZU:
                    matrix = Rotate(2, 3, v);
                    break;
                case TransformEnum.Scale:
                    matrix = Scale(v);
                    break;
                default:
                    LoadEdintical(matrix);
                    break;    
            }

            Matrix result = matrix * _matrix;
            _matrix = result;
        }

        /// <summary>
        /// Параллельный перенос коллекции векторов.
        /// </summary>
        /// <param name="vectors"></param>
        /// <param name="dv"></param>
        static public void Translate(Vector[] vectors, Vector dv)
        {
            for (int i = 0; i < vectors.Count(); i++)
            {
                vectors[i] += dv;
            }
        }

        /// <summary>
        /// Получение результирующей матрицы.
        /// </summary>
        /// <returns>Результирующая матрица.</returns>
        public Matrix End()
        {
            return _matrix;
        }
    }
}
